#include <stdio.h>
#include <vector>
#include <iostream>
#include <algorithm>

#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

#include "logger.hpp"

class MappedFile {
    public:
        enum Mode {
            READ = 0,
            WRITE = 1
        };
        MappedFile() {
        }
        ~MappedFile() {
        }

        bool Initialize(const char* file_name, Mode mode) {
            mode_ = mode;
            fd_ = open(file_name, (mode_ == READ)?O_RDONLY:O_RDWR|O_TRUNC);

            if(fd_ == -1) {
                LogEr("can't open file");
                return false;
            }
            struct stat file_stat;
            if(fstat(fd_, &file_stat) < 0) {
                LogEr("can't fstat file");
                close(fd_);
                return false;
            }
            size_of_file_ = file_stat.st_size;
    
            size_of_page_ = sysconf(_SC_PAGE_SIZE);
            if(size_of_page_ == -1) {
                LogEr("falled sysconf()");
                close(fd_);
                return false;
            }
            number_of_opened_page = -1;
            return true;
        }
        
        bool Finalize() {
            if(close(fd_) == -1) {
                LogEr("can't close file");
                return false;
            }
            if(number_of_opened_page != -1) {
                int rv = munmap(mapped_part_, size_of_page_);
                if(rv == -1) {
                    LogEr("Can't munmap");
                }
            }
            return true;
        }

        char& operator[] (long i) {
            if(i >= size_of_file_) {
                int rv = ftruncate(fd_, size_of_page_ * ((i / size_of_page_) + 1));
                if(rv == -1) {
                    LogEr("failed ftrancate");
                }
                size_of_file_ = size_of_page_ * ((i / size_of_page_) + 1);
            }
            
            if(i < size_of_page_ * number_of_opened_page || i >= size_of_page_ *    
                (number_of_opened_page + 1)) { 
        
                if(number_of_opened_page != -1) {
                    munmap(mapped_part_, size_of_page_);
                }
            
                mapped_part_ = (char*)mmap(NULL, size_of_page_, (mode_ == READ)?PROT_READ:
                    PROT_WRITE, MAP_SHARED, fd_, size_of_page_ * (i / size_of_page_));
                
 
                number_of_opened_page = i / size_of_page_;
            }
            return mapped_part_[i - (size_of_page_ * number_of_opened_page)];
        }

        int GetSizeOfFile() {
            return size_of_file_;
        }
        
        
    private:
        long size_of_page_;
        int size_of_file_;
        int fd_;
        char* mapped_part_;
        int number_of_opened_page; //my brilliant solution
        Mode mode_;
};

bool GetNumbers(MappedFile& array, int size_of_array, std::vector<int>* result) {
    result->clear();
    char ch;
    std::string number;
    int current_position_in_array = 0;
    while(true) {
        if(current_position_in_array == size_of_array) return true;
        ch = array[current_position_in_array];
        current_position_in_array++;

        if(isdigit(ch)) {
            number.push_back(ch);
            continue;
        }
        if(number.size() == 0) continue;
        if(number.size() > 10) {
            LogEr("Too big numbers");
            return false;
        }
        char* end_ptr;

        long int num = strtol(number.c_str(), &end_ptr, 10);
        if(*end_ptr != '\0' || errno == ERANGE) {
            LogEr("Incorect array");
            return false;
        }
        result->push_back((int)num);
        number.clear();
    }
}


int main(int argc, char** argv) {
    if(argc != 3) return -1;
    if(argv[1] == argv[2]) return -1;
    Logger::Instance().Initialize("Log.txt");
    
    MappedFile input;
    if(!input.Initialize(argv[1], MappedFile::READ)) return -1;

    int size = input.GetSizeOfFile();

    std::vector<int> array;
    int number;
    if(!GetNumbers(input, size, &array)) {
        return -1;
    }
    std::sort(array.begin(), array.end());
    char buffer[10];
    int current_position_in_array = 0;
    MappedFile output;
    if(!output.Initialize(argv[2], MappedFile::WRITE)) return -1;

    for(int i = 0; i < array.size(); i++) {
        sprintf(buffer, "%d,\0", array[i]);
        int len = strlen(buffer);
        for(int j = 0; j < len; j++) {
            output[current_position_in_array + j] = buffer[j];//crashed here
        }
        current_position_in_array += len;        
    }
}

